#include <filesystem>
#include <thread>
#include "ProtocolApi.h"
#include "DeviceManager.h"
#include "HidDeviceManager.h"
#include "A15Device.h"
#include "A20Device.h"
#include "TailDevice.h"
#include "V520DDevice.h"
#include "Util/logc.h"
#include "bin_parser.h"
#include <stdio.h>
#include <stdlib.h>

using namespace Protocol;


HidDeviceManager::Ptr HidDeviceManager::Instance(){
    static auto ptr = std::make_shared<HidDeviceManager>();
    return ptr;
}


HidDeviceManager::HidDeviceManager(){
    m_checkDeviceTask = std::async(std::launch::async, [this](){deviceCheck();});
}


HidDeviceManager::~HidDeviceManager(){
    m_cv.notify_one();
    m_checkDeviceTask.wait();
    tracef(" ~HidDeviceManager");
}

bool HidDeviceManager::Upgrade(const std::string& serial_number, const std::string& upgrade_file, 
    std::function<void (const std::string, int )> notifyProgress){
    HidDevice::Ptr upgrade_device;
    {    
        std::lock_guard<std::recursive_mutex> guard(m_mux);
        if(m_devices.find(serial_number) != m_devices.end()){
            std::string product_name;
            std::string product_model;
            std::string phy_version; 
            std::string soft_version; 
            std::string sn; 
            std::string unique_id;
            std::vector<HidDevInfo> sub_device;
            if(m_devices[serial_number]->GetDeviceBasicInfo(product_name, product_model, phy_version, soft_version, sn, unique_id, sub_device)){
                //Currently only support A15
                if(product_name.find("A15") == product_name.npos && product_name.find("AW20") == product_name.npos && product_name.find("V520D") == product_name.npos){
                    errorf("Currently only support A15 %s", product_name.c_str());
                    return false;
                }else{
                    upgrade_device = m_devices[serial_number];
                }
            }

        }else{
            if(notifyProgress) notifyProgress(serial_number, -1);
            return false;
        }
    }
    if(upgrade_device){
        bool upgrade_done = false;
        bool upgrade_ret = false;
        std::thread th([&upgrade_done, &upgrade_ret, upgrade_device, upgrade_file]{
            upgrade_ret = upgrade_device->Upgrade(upgrade_file);
            upgrade_done = true;
        });

        th.detach();
        while(!upgrade_done){
            int progress = upgrade_device->GetUpgradeProgress();
            tracef(" !!! progress %d", progress);
            if(notifyProgress) notifyProgress(serial_number, progress);
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }


        if(upgrade_ret){
            if(notifyProgress) notifyProgress(serial_number, 100);
            return true;
        }else{
            if(notifyProgress) notifyProgress(serial_number, -1);
        }
    }

    return false;
}

bool HidDeviceManager::Reboot(const std::string& serial_number){
    std::lock_guard<std::recursive_mutex> guard(m_mux);
    if(m_devices.find(serial_number) != m_devices.end()){
         return m_devices[serial_number]->Reboot();
    }
    return false;
}

bool HidDeviceManager::Reset(const std::string& serial_number){
    std::lock_guard<std::recursive_mutex> guard(m_mux);
    if(m_devices.find(serial_number) != m_devices.end()){
         return m_devices[serial_number]->Reset();
    }
    return false;
}

std::vector<HidDeviceInfo> HidDeviceManager::GetHidDeviceList(){
    std::lock_guard<std::recursive_mutex> guard(m_mux);
    std::vector<HidDeviceInfo> devices;
    for(auto it : m_devices){
        Json::Value device;
        std::string product_name;
        std::string product_model;
        std::string phy_version; 
        std::string soft_version; 
        std::string serial_number; 
        std::string unique_id;
        std::vector<HidDevInfo> sub_devices;
        HidDeviceInfo info;
        if(it.second->GetDeviceBasicInfo(product_name, product_model, phy_version, soft_version, serial_number, unique_id, sub_devices)){
            info.m_product_name= product_name;
            info.m_phy_version = phy_version;
            info.m_soft_version = soft_version;
            info.m_serial_number = serial_number;
            info.m_unique_id= unique_id;
            info.m_product_model = product_model;
            info.m_sub_devices = sub_devices;
            devices.push_back(info);
        }
    }

    return devices;
}


bool HidDeviceManager::getCmdInfo(char *cmd, std::vector<std::string> &info){
	if(cmd == NULL || strlen(cmd) == 0){
		errorf("cmd is empty\n");
		return false;
	}
	char buf[1024] = {0};
	FILE *fp = popen(cmd, "r");
	if(fp)
	{
		while(fgets(buf, sizeof(buf), fp))
		{	
			info.push_back(buf);
			//LOG_PrintInfo("buf --> %s\n", buf);

			memset(buf, 0, sizeof(buf));
		}
		pclose(fp);
	}else{
		errorf("process cmd is error:%s\n", cmd);
		return false;
	}
#if 0

	for(int i = 0; i < info.size(); i++){

		errorf("buf --> %s\n", info[i].c_str());

	}

#endif

	return true;
}

void HidDeviceManager::deviceCheck(){
    //test /dev/hidraw0  ~ /dev/hidraw50
    std::this_thread::sleep_for(std::chrono::seconds(5));
    while(true){
        std::string product_name;
        std::string product_model;
        std::string phy_version; 
        std::string soft_version; 
        std::string serial_number; 
        std::string unique_id;
        std::vector<HidDevInfo> sub_devices;
        HidDevInfo mainDev;
        bool change = false;
        for(int i = 0; i < 50; i++){
            std::lock_guard<std::recursive_mutex> guard(m_mux);
            std::string device_path = HID_DEVICE_PREFIX_NAME + std::to_string(i);

            if(std::filesystem::exists(device_path)){
                char cmd[64] = {0};
                char msg[256] = {0};
                std::vector<std::string> info;
                //先将Tail过滤出来使用
                const std::string ROOMS_MATCH_TAIL_VIDPID = "0627:A6A0";
                const std::string HidInfoCmd = "udevadm info %s";
                sprintf(cmd, HidInfoCmd.c_str(), device_path.c_str());

                if(getCmdInfo(cmd, info)){
                    if(info.size() >= 4){
                        if(strstr(info[3].c_str(), ROOMS_MATCH_TAIL_VIDPID.c_str()) == NULL){
                            continue;
                        }
                    }else{
                        continue;
                    }
                }	

                bool isUpgrading = false;
                for(auto device: m_devices){
                    if(device.second->GetDevicePath() == device_path){
                        if(device.second->IsUpgrading()){
                            isUpgrading = true;
                        }
                    }
                }
                
                if(!isUpgrading){
                    mainDev.mainDevice = false;
                    if(GetDeviceBasicInfo(device_path, sub_devices)){
                        for(int i = 0; i < sub_devices.size(); i++){
                            if(sub_devices[i].mainDevice){
                                mainDev = sub_devices[i];
                                product_name  = mainDev.m_product_name;
                                phy_version = mainDev.m_phy_version;
                                soft_version = mainDev.m_soft_version;
                                serial_number = mainDev.m_serial_number;
                                unique_id = mainDev.m_unique_id;
                                
                                auto it = sub_devices.begin();
                                sub_devices.erase(it+i);
                            }
                        }
                        if(!mainDev.mainDevice){
                            errorf("mainDev.mainDevice false\n");
                            continue;
                        }

                        if(m_devices.find(serial_number) == m_devices.end()){
                            if(product_name.find("A15") != product_name.npos){
                                product_model = "A15";
                                m_devices[serial_number] = std::make_shared<A15Device>(device_path,product_model,product_name, 
                                                phy_version, soft_version, serial_number, unique_id, sub_devices);
                            }else if(product_name.find("Tail") != product_name.npos || product_name.find("tail") != product_name.npos){
                                product_model = "Tail";
                                std::shared_ptr<TailDevice> tailObj = std::make_shared<TailDevice>(device_path,product_model,product_name, 
                                                phy_version, soft_version, serial_number, unique_id, sub_devices);
                                m_devices[serial_number] = tailObj;
                            }else if(product_name.find("AW20") != product_name.npos){
                                product_model = "a20-hamedal";
                                tracef("------------------------------------------------------AW20 sub_devices.size(): %d\n", sub_devices.size());
                                std::shared_ptr<A20Device> tailObj = std::make_shared<A20Device>(device_path,product_model,product_name, 
                                                phy_version, soft_version, serial_number, unique_id, sub_devices);
                                m_devices[serial_number] = tailObj;
                            }else if(product_name.find("V520D") != product_name.npos){
                                product_model = "V520D";
                                std::shared_ptr<V520DDevice> tailObj = std::make_shared<V520DDevice>(device_path,product_model,product_name, 
                                                phy_version, soft_version, serial_number, unique_id, sub_devices);
                                m_devices[serial_number] = tailObj;
                            }

                            DeviceManager::Instance()->EventUpdateDevice(product_name, product_model,
                                phy_version, soft_version, serial_number, "add");
                           
                            tracef("add HidDevices product_name %s, phy_version %s,\
                            soft_version %s, serial_number %s", 
                            product_name.c_str(), phy_version.c_str(), 
                            soft_version.c_str(), serial_number.c_str());
                            change = true;
                        }else{
                            //后续直接对比sub_device就可以查看当前外接设备信息是否有变化，这里需要修改上面代码不删除主设备遍历子设备时需要跳过主设备
                            m_devices.find(serial_number)->second->UpdateDevice(device_path,product_name, 
                                                phy_version, soft_version, serial_number, unique_id, sub_devices);
                        }
                    }else{
                        tracef(" GetDeviceBasicInfo fail  %s ", device_path.c_str());
                    }
                }else{
                    errorf("the device %s isUpgrading", device_path.c_str());
                }
            }else{
                for(auto device: m_devices){
                    if(device.second->GetDevicePath() == device_path){
                        if(device.second->GetDeviceBasicInfo(product_name, product_model,
                            phy_version, soft_version, serial_number, unique_id, sub_devices)){
                               
                                tracef("remove HidDevices product_name %s, phy_version %s,\
                                    soft_version %s, serial_number %s", 
                                product_name.c_str(), phy_version.c_str(), 
                                soft_version.c_str(), serial_number.c_str());
                                DeviceManager::Instance()->EventUpdateDevice(product_name, product_model,
                                phy_version, soft_version, serial_number, "remove");
                                change = true;
                        }

                        m_devices.erase(device.first);//remove
                        break;
                    }
                }
            }
        }

        if(change){
            DeviceManager::Instance()->UpdateDeviceList();
        }
        std::unique_lock<std::mutex> lock(m_cv_mux);
        auto wait_status = m_cv.wait_for(lock, std::chrono::seconds(3));
        if(wait_status == std::cv_status::no_timeout){
            tracef(" deviceCheck  exit");
            break;
        }
    }
    
    // std::string product_name;
    // std::string phy_version; 
    // std::string soft_version; 
    // std::string serial_number; 
    // std::string unique_id;


    // if(GetDeviceBasicInfo("/dev/hidraw0", product_name, 
    //         phy_version, soft_version, serial_number, unique_id)){

    //     tracef("GetDeviceBasicInfo product_name %s, phy_version %s,\
    //     soft_version %s, serial_number %s, unique_id %s", product_name.c_str(), 
    //         phy_version.c_str(), soft_version.c_str(), 
    //         serial_number.c_str(), unique_id.c_str());
    
    // }

    // auto hidDevice = std::make_shared<A15Device>("/dev/hidraw0",product_name, 
    //         phy_version, soft_version, serial_number, unique_id);
   

    // if(hidDevice->GetDeviceBasicInfo(product_name, 
    //         phy_version, soft_version, serial_number, unique_id)){
    //     tracef("GetDeviceBasicInfo product_name %s, phy_version %s,\
    //     soft_version %s, serial_number %s, unique_id %s", product_name.c_str(), 
    //         phy_version.c_str(), soft_version.c_str(), 
    //         serial_number.c_str(), unique_id.c_str());
    // }

    // auto lam = [hidDevice](){
    //     hidDevice->Upgrade("/nfsroot/General_MC_[Hamedal_A15]_V1.100.1.9.R.231115_ota.bin");
    // };

    // std::thread th(lam);
    // th.detach();

    // while(true){
    //     int progress = hidDevice->GetUpgradeProgress();
    //     tracef(" !!!!  UpgradeProgress %d ",progress);
    //     if(progress >= 100) break;
    //     std::this_thread::sleep_for(std::chrono::seconds(1));
    // };
    // hidDevice->Upgrade("/nfsroot/General_MC_[Hamedal_A15]_V1.100.1.9.R.231115_ota.bin");
}
